package ws

import (
	"blockchain_data/middleware"
	"bytes"
	"compress/flate"
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"
)
var log *logrus.Logger
func init()  {
	log = middleware.DataLogger()
}
type MethonInfo struct {
	Fun   interface{}
	Param reflect.Type
}

func (wsOp *MethonInfo) Init(fun interface{}, param reflect.Type) *MethonInfo {
	wsOp.Fun = fun
	wsOp.Param = param
	return wsOp
}

type WebSocketOp struct {
	Host string						//根路径
	Path string						//子路径
	Conn      *websocket.Conn

	Isclose chan bool
	Reconnect chan bool


	SubStr []byte
	Fun    interface{}
	Param  reflect.Type
	Methon MethonInfo

	IsPong chan string
	PingTime time.Duration
	Heart bool						//是否需要主动发送心跳
	StopHeart  chan int
}

func (wsOp *WebSocketOp) Open(path string, host string, heart bool,pingTime time.Duration) bool {
	wsOp.Host = host
	wsOp.Path = path
	wsOp.Heart = heart
	if(wsOp.Heart){
		wsOp.PingTime = pingTime
	}

	wsOp.Reconnect = make(chan bool)
	wsOp.IsPong = make(chan string)
	wsOp.Isclose = make(chan bool,16)
	ret := wsOp.ConnServer()
	return ret
}

func (wsOp *WebSocketOp) Close() {
	wsOp.Conn.Close()
	wsOp.Conn = nil
	wsOp.Isclose <- true
}

func (wsOp *WebSocketOp) ConnServer() bool {
	url := fmt.Sprintf("wss://%s%s", wsOp.Host, wsOp.Path)
	fmt.Println(url)
	var err error
	wsOp.Conn, _, err = (&websocket.Dialer{
		Proxy:            http.ProxyFromEnvironment,
		HandshakeTimeout: 45 * time.Second,
	}).Dial(url, nil)

	if err != nil {
		log.Error("WebSocket 连接错误: %s", err)
		return false
	}
	log.Info("WebSocket 连接成功")
	wsOp.Conn.SetCloseHandler(wsOp.OnClose)

	if(wsOp.Heart){
		go wsOp.HeartWork()
	}else {
		go wsOp.BaseWork()
	}
	go wsOp.ReadLoop()
	go wsOp.Finalize()
	return true
}

func (wsOp *WebSocketOp) OnClose(code int, text string) error {
	log.Info("WebSocket 异常断开，尝试重新连接。。。")
	wsOp.Conn = nil
	wsOp.Reconnect <- true
	return fmt.Errorf("WebSocket 异常断开，尝试重新连接。。。")
}

// BaseWork 不需要主动发心跳的work
func (wsOp *WebSocketOp)BaseWork()  {
	for {
		select {
		case <-wsOp.Reconnect:
			conn := wsOp.ConnServer()
			time.Sleep(3 * time.Second)
			if(!conn){
				//两秒后重连
				wsOp.Reconnect <- true
			}else {
				wsOp.Sub(wsOp.SubStr,wsOp.Fun,wsOp.Param)
			}
		case <-wsOp.Isclose:
			return
		}
	}
}

// HeartWork 需要主动发心跳的work
func (wsOp *WebSocketOp)HeartWork()  {
	//每20秒发一次ping
	ticker := time.NewTicker(wsOp.PingTime)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			go wsOp.Ping()
		case <-wsOp.Reconnect:
			conn := wsOp.ConnServer()
			time.Sleep(3 * time.Second)
			if(!conn){
				//两秒后重连
				wsOp.Reconnect <- true
			}else {
				wsOp.Sub(wsOp.SubStr,wsOp.Fun,wsOp.Param)
			}
		case <-wsOp.Isclose:
			return
		}
	}
}

// Finalize 结束需要检查通道是否都关闭
func (wsOp *WebSocketOp) Finalize() error {
	defer func() {
		log.Printf("关闭订阅的连接",wsOp.SubStr)
	}()
	select {
	case <-wsOp.Isclose:
		if wsOp.Conn != nil {
			close(wsOp.Isclose)
			close(wsOp.Reconnect)
			return wsOp.Conn.Close()
		}
	}
	return nil
}

// Ping 如果有需要主动发送心跳重写
func (wsOp *WebSocketOp) Ping()  {
	msg := "ping"
	//log.Printf("Send Msg: %s", msg)
	wsOp.Conn.WriteMessage(websocket.TextMessage, []byte(msg))
	go wsOp.Pong()
}

// Pong 响应
func (wsOp *WebSocketOp) Pong()  {
	//如果20秒内没有pong就重新连接
	ticker := time.NewTicker(wsOp.PingTime)
	select {
	case <-ticker.C:
		wsOp.Reconnect <- true
	case <-wsOp.IsPong:
		return
	}
}

// 必须重写
func (wsOp *WebSocketOp) ReadLoop() {
	for wsOp.Conn != nil{
		messageType, message, err := wsOp.Conn.ReadMessage()
		if err != nil {
			log.Errorf("数据获取异常: %s", err)
			return
		}
		txtMsg := message
		switch messageType {
		case websocket.TextMessage:
		case websocket.BinaryMessage:
			txtMsg, err = wsOp.GzipDecode(message)
		}
		fmt.Println(string(txtMsg))
	}
}

func (wsOp *WebSocketOp) HandleSubCallbackFun(data string) {
	var mi *MethonInfo = nil
	mi = new(MethonInfo).Init(wsOp.Fun, wsOp.Param)

	wsOp.RunFunction(mi, data)
}

func (wsOp *WebSocketOp) RunFunction(mi *MethonInfo, data string) {
	param := reflect.New(mi.Param).Interface()
	json.Unmarshal([]byte(data), &param)

	rargs := make([]reflect.Value, 1)

	rargs[0] = reflect.ValueOf(param)
	fun := reflect.ValueOf(mi.Fun)
	fun.Call(rargs)
}

func (wsOp *WebSocketOp) Sub(subStr []byte, fun interface{}, param reflect.Type) bool {
	wsOp.SubStr = subStr
	wsOp.Fun = fun
	wsOp.Param = param
	wsOp.Conn.WriteMessage(websocket.TextMessage, subStr)


	return true
}

func (wsOp *WebSocketOp) Unsub(unsubStr []byte) bool {
	wsOp.Conn.WriteMessage(websocket.TextMessage, unsubStr)
	return true
}

func (wsOp *WebSocketOp) GzipDecode(in []byte) ([]byte, error) {
	reader := flate.NewReader(bytes.NewReader(in))
	defer reader.Close()

	return ioutil.ReadAll(reader)
}

